package com.hqd.ch03.v40.web.servlet.view;

import com.hqd.ch03.utils.CollectionUtils;
import com.hqd.ch03.utils.PatternMatchUtils;
import com.hqd.ch03.v40.core.Ordered;
import com.hqd.ch03.v40.utils.BeanUtils;
import com.hqd.ch03.v40.web.servlet.View;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered {
    /**
     * 重定向
     */
    public static final String REDIRECT_URL_PREFIX = "redirect:";
    /**
     * 转发
     */
    public static final String FORWARD_URL_PREFIX = "forward:";

    private final Map<String, Object> staticAttributes = new HashMap<>();
    private Class<?> viewClass;
    private String prefix = "";
    private String suffix = "";

    private String contentType;
    private boolean redirectContextRelative = true;
    private boolean redirectHttp10Compatible = true;

    private String[] redirectHosts;

    private String requestContextAttribute;
    /**
     * 支持的视图名
     */
    private String[] viewNames;
    /**
     * 是否开启路径匹配
     */
    private Boolean exposePathVariables;

    /**
     * 暴露容器中的bean
     */
    private Boolean exposeContextBeansAsAttributes;


    private String[] exposedContextBeanNames;


    private int order = Ordered.LOWEST_PRECEDENCE;

    protected Class<?> getViewClass() {
        return this.viewClass;
    }

    public void setViewClass(Class<?> viewClass) {
        if (viewClass != null && !requiredViewClass().isAssignableFrom(viewClass)) {
            throw new IllegalArgumentException("Given view class [" + viewClass.getName() +
                    "] is not of type [" + requiredViewClass().getName() + "]");
        }
        this.viewClass = viewClass;
    }

    protected String getPrefix() {
        return this.prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = (prefix != null ? prefix : "");
    }

    protected String getSuffix() {
        return this.suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = (suffix != null ? suffix : "");
    }

    protected String getContentType() {
        return this.contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    protected boolean isRedirectContextRelative() {
        return this.redirectContextRelative;
    }

    public void setRedirectContextRelative(boolean redirectContextRelative) {
        this.redirectContextRelative = redirectContextRelative;
    }

    protected boolean isRedirectHttp10Compatible() {
        return this.redirectHttp10Compatible;
    }

    public void setRedirectHttp10Compatible(boolean redirectHttp10Compatible) {
        this.redirectHttp10Compatible = redirectHttp10Compatible;
    }


    public String[] getRedirectHosts() {
        return this.redirectHosts;
    }

    public void setRedirectHosts(String... redirectHosts) {
        this.redirectHosts = redirectHosts;
    }

    protected String getRequestContextAttribute() {
        return this.requestContextAttribute;
    }

    public void setRequestContextAttribute(String requestContextAttribute) {
        this.requestContextAttribute = requestContextAttribute;
    }

    public void setAttributes(Properties props) {
        CollectionUtils.mergePropertiesIntoMap(props, this.staticAttributes);
    }

    public Map<String, Object> getAttributesMap() {
        return this.staticAttributes;
    }

    public void setAttributesMap(Map<String, ?> attributes) {
        if (attributes != null) {
            this.staticAttributes.putAll(attributes);
        }
    }


    protected Boolean getExposePathVariables() {
        return this.exposePathVariables;
    }

    public void setExposePathVariables(Boolean exposePathVariables) {
        this.exposePathVariables = exposePathVariables;
    }


    protected Boolean getExposeContextBeansAsAttributes() {
        return this.exposeContextBeansAsAttributes;
    }

    public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
        this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
    }


    protected String[] getExposedContextBeanNames() {
        return this.exposedContextBeanNames;
    }

    public void setExposedContextBeanNames(String... exposedContextBeanNames) {
        this.exposedContextBeanNames = exposedContextBeanNames;
    }


    protected String[] getViewNames() {
        return this.viewNames;
    }

    public void setViewNames(String... viewNames) {
        this.viewNames = viewNames;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }


    @Override
    protected Object getCacheKey(String viewName, Locale locale) {
        return viewName;
    }

    @Override
    protected View createView(String viewName, Locale locale) throws Exception {
        /**
         * 是否能够处理
         */
        if (!canHandle(viewName, locale)) {
            return null;
        }

        return super.createView(viewName, locale);
    }

    protected Class<?> requiredViewClass() {
        return AbstractUrlBasedView.class;
    }

    protected boolean canHandle(String viewName, Locale locale) {
        String[] viewNames = getViewNames();
        /**
         * 是否限制处理视图名，如果有限制则查看是否匹配
         */
        return (viewNames == null || PatternMatchUtils.simpleMatch(viewNames, viewName));
    }

    protected AbstractUrlBasedView instantiateView() {
        Class<?> viewClass = getViewClass();
        return (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
    }

    @Override
    protected View loadView(String viewName, Locale locale) throws Exception {
        AbstractUrlBasedView view = buildView(viewName);
        View result = applyLifecycleMethods(viewName, view);
        return (view.checkResource(locale) ? result : null);
    }

    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        AbstractUrlBasedView view = instantiateView();
        view.setUrl(getPrefix() + viewName + getSuffix());
        view.setAttributesMap(getAttributesMap());

        String contentType = getContentType();
        if (contentType != null) {
            view.setContentType(contentType);
        }

        String requestContextAttribute = getRequestContextAttribute();
        if (requestContextAttribute != null) {
            view.setRequestContextAttribute(requestContextAttribute);
        }

        Boolean exposePathVariables = getExposePathVariables();
        if (exposePathVariables != null) {
            view.setExposePathVariables(exposePathVariables);
        }
        Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
        if (exposeContextBeansAsAttributes != null) {
            view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
        }
        String[] exposedContextBeanNames = getExposedContextBeanNames();
        if (exposedContextBeanNames != null) {
            view.setExposedContextBeanNames(exposedContextBeanNames);
        }

        return view;
    }

    protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {
        return view;
    }

}

